package com.junko.admin.interceptor;

import com.junko.admin.configuration.MyConfig;
import com.junko.admin.tool.ToolIDEA;
import com.junko.core.constant.ConstantWebContext;
import com.junko.core.exception.BusinessException;
import com.junko.core.exception.LockedException;
import com.junko.core.exception.NoLoginException;
import com.junko.core.exception.NoPermissionException;
import com.junko.core.tool.ToolDateTime;
import com.junko.core.tool.ToolSystem;
import com.junko.core.tool.ToolWeb;
import com.junko.model.back.*;
import com.junko.model.dto.MenuDto;
import com.junko.service.back.*;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * 后台访问拦截器
 * @author junko
 */
public class BackendInterceptor extends HandlerInterceptorAdapter {
    private static final Logger logger = LogManager.getLogger(BackendInterceptor.class);
    @Resource
    private IAdminService adminService;
    @Resource
    private IMenuService menuService;
    @Resource
    private ISiteService siteService;
    @Resource
    private IOperatorService operatorService;
    @Resource
    private IGroupService groupService;
    @Resource
    private IRoleService roleService;
    @Resource
    private ISyslogService syslogService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String actionKey = ToolWeb.getActionKey(request);
        //访问静态资源或特定url
        if (StringUtils.startsWith(actionKey, "/b/assets/")) {
            return true;
        }
        logger.info("获取当前用户!");
        boolean userAgentVali = true; // 是否验证userAgent，默认是
        if (actionKey.equals("/b/ueditor") || actionKey.equals("/b/upload")) { // 针对ueditor、upload特殊处理
            userAgentVali = false;
        }
        Admin admin = getCurrentAdmin(request,response,userAgentVali);

        Syslog reqSyslog = (Syslog) request.getAttribute(ConstantWebContext.reqSyslogKey);

        if (null != admin) {
            reqSyslog.setAdminId(admin.getId());
            request.setAttribute(ConstantWebContext.request_cUser, admin);
            request.setAttribute(ConstantWebContext.request_cUserId, admin.getId());

            MDC.put("userId", admin.getId()+"");
            MDC.put("userName", admin.getAccount());
        }else{
            MDC.put("userId", "*unknown userId*");
            MDC.put("userName", "*unknown userName*");
        }
        Object operatorObj = operatorService.findByUrl(actionKey);

        if (null == operatorObj) {
            logger.info("URI不存在->"+actionKey);

            logger.info("访问失败时保存日志!");
            reqSyslog.setStatus("0");// 失败
            reqSyslog.setDescription("URL不存在");
            reqSyslog.setCause("1");// URL不存在

            logger.info("返回失败提示页面!");
            throw new BusinessException("URI不存在");
        }

        Operator operator = (Operator) operatorObj;
        reqSyslog.setOperatorId(operator.getId());

        if (operator.getPrivilege()) {// 是否需要权限验证
            logger.info("需要权限验证!");
            if (admin == null) {
                logger.info("权限认证过滤器检测:未登录!");

                reqSyslog.setStatus("0");// 失败
                reqSyslog.setDescription("未登录");
                reqSyslog.setCause("2");// 2 未登录

                throw new NoLoginException();
            }

            if (!hasPrivilegeUrl(admin.getId(), actionKey)) {// 权限验证
                logger.info("权限验证失败，没有权限!");

                reqSyslog.setStatus("0");// 失败
                reqSyslog.setDescription("没有权限!");
                reqSyslog.setCause("0");// 没有权限

                throw new NoPermissionException();
            }
        }

        reqSyslog.setStatus("1");// 成功
        Date actionStartDate = ToolDateTime.getDate();// action开始时间
        reqSyslog.setActionStartDate(ToolDateTime.getSqlTimestamp(actionStartDate));
        reqSyslog.setActionStartTime(actionStartDate.getTime());

//        //线上环境记录日志
//        if(ToolSystem.isOnline()){
//            syslogService.insertSelective(reqSyslog);
//        }

        MDC.remove("userId");
        MDC.remove("userName");

        //如果不是异步请求
        if(!ToolWeb.isAsync(request)){
            if (admin == null) {
                response.sendRedirect("/b/login");
                return false;
            }
            if(admin.getLocked()){
                throw new LockedException();
            }
            request.setAttribute("actionKey",actionKey);
            request.setAttribute("cxt",ToolWeb.getContextPath(request));
            Map<String, Cookie> cookieMap = ToolWeb.readCookieMap(request);
            request.setAttribute(ConstantWebContext.request_cookieMap, cookieMap);

            //获取用户的权限菜单
            List<Menu> parentMenus = menuService.findTopParentsBySystemId(1);//顶级菜单
            List<MenuDto> result = new ArrayList<>();
            //系统权限、系统维护
            List<Menu> menus = menuService.findByParentId(parentMenus.get(0).getId());//直接子菜单
            MenuDto menuDto;
            for (Menu temp : menus) {
                menuDto = new MenuDto();
                menuDto.setSelf(temp);
                menuDto.setChildren(getResultByParentMenu(admin,temp));
                result.add(menuDto);
            }
            request.setAttribute("menuList",result);
            Site site = siteService.findOnlyOne();
            site.setLogo(site.getLogo());
            request.setAttribute("site",site);
        }
        return true;
    }

    /**
     * 根据父菜单递归渲染子菜单
     * @param menu
     * @return
     */
    private List<MenuDto> getResultByParentMenu(Admin admin, Menu menu){
        List<MenuDto> result = new ArrayList<>();
        //查询直接子菜单
        List<Menu> menus = menuService.findByParentId(menu.getId());
        MenuDto menuDto;
        //子菜单不为空时
        for (Menu temp : menus) {
            //查询对应的 功能
            Operator operator = operatorService.selectByPrimaryKey(temp.getOperatorId());
            if(operator!=null){
                if(!hasPrivilegeUrl(admin.getId(),operator.getUrl())){
                    continue;
                }
            }
            menuDto = new MenuDto();
            menuDto.setSelf(temp);
            if(temp.getParentId()==null){
                menuDto.setChildren(getResultByParentMenu(admin,temp));
            }else{
                menuDto.setUrl(operator==null?null:operator.getUrl());
            }
            result.add(menuDto);
        }
        return result;
    }

    /**
     * 判断用户是否拥有某个url的操作权限
     *
     * @param adminId
     * @param url
     * @return
     */
    public boolean hasPrivilegeUrl(Integer adminId, String url){
        // 基于缓存查询operator
        Operator operator = operatorService.findByUrl(url);
        if (null == operator) {
            logger.error("访问的URL不存在：" + url);
            return false;
        }
        // 基于缓存查询admin
        Object adminObj = adminService.selectByPrimaryKey(adminId);
        if (null == adminObj) {
            logger.error("管理员不存在：" + adminId);
            return false;
        }
        Admin admin = (Admin) adminObj;

        // 权限验证对象
        String operatorId = operator.getId() + ",";
        String groupId = admin.getGroupIds();

        // 根据分组查询权限
        if (StringUtils.isNotBlank(groupId)) {
            String[] groupIdArr = groupId.split(",");
            for (String groupIdTemp : groupIdArr) {
                Group group = groupService.selectByPrimaryKey(Integer.parseInt(groupIdTemp));
                String roleIdStr = group.getRoleIds();
                if (null == roleIdStr || roleIdStr.equals("")) {
                    continue;
                }
                String[] roleIdArr = roleIdStr.split(",");
                for (String roleIdTemp : roleIdArr) {
                    Role role = roleService.selectByPrimaryKey(Integer.parseInt(roleIdTemp));
                    if(role!=null){
                        String operatorIdStr = role.getOperatorIds();
                        if (StringUtils.isNotBlank(operatorIdStr)&&operatorIdStr.contains(operatorId)) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    /**
     * 获取当前登录用户
     * @param response
     * @return
     */
    protected Admin getCurrentAdmin(HttpServletRequest request,HttpServletResponse response,boolean userAgentVali) {
        // 加密串存储位置，默认先取header
        String authmark = request.getHeader(ConstantWebContext.cookie_authmark);

        // 如果为空，再取cookie
        if (StringUtils.isEmpty(authmark)) {
            authmark = ToolWeb.getCookieValueByName(request, ConstantWebContext.cookie_authmark);
        }

        // 登录cookie标识异常
        if (StringUtils.isEmpty(authmark)) {
            ToolWeb.addCookie(request, response, "", null, true, ConstantWebContext.cookie_authmark, null, 0);
            return null;
        }

        // 1.解密认证数据，外层
        String outer = ToolIDEA.decrypt(MyConfig.config_securityKey_idea,authmark);
        if (StringUtils.isEmpty(outer)) {
            ToolWeb.addCookie(request, response, "", null, true, ConstantWebContext.cookie_authmark, null, 0);
            return null;
        }
        String[] outerArr = outer.split(".#.");    // arr[0]：USERID，arr[1]：inner
        String adminId = outerArr[0];
        Admin admin = adminService.selectByPrimaryKey(Integer.valueOf(adminId));
        if (admin == null) {
            return null;
        }
        String secretkey = admin.getIdea();
        String inner = ToolIDEA.decrypt(secretkey, outerArr[1]);

        // 2. 分解认证数据，内层
        String[] innerArr = inner.split(".#.");        // arr[0]：scheme，arr[1]：时间戳，arr[2]：USER_IP， arr[3]：USER_AGENT， arr[4]：autoLogin
        String scheme = innerArr[0];                            // scheme
        long loginDateTimes = Long.parseLong(innerArr[1]);        // 时间戳
        String ips = innerArr[2];                                // ip地址
        String userAgent = innerArr[3];                        // USER_AGENT
        boolean autoLogin = Boolean.valueOf(innerArr[4]);        // 是否自动登录

        // 3.用户当前数据
        String newScheme = request.getScheme();
        String newIp = ToolWeb.getIpAddr(request);
        String newUserAgent = request.getHeader("User-Agent");

        Date start = ToolDateTime.getDate();
        start.setTime(loginDateTimes); // 用户自动登录开始时间
        int day = ToolDateTime.getDateDaySpace(start, ToolDateTime.getDate()); // 已经登录多少天
        int maxAge = MyConfig.config_cookie_maxAge;    // cookie自动登录有效天数

        // 4. 验证数据有效性
        if (scheme.equals(newScheme)
                && ips.equals(newIp)
                && (userAgent.equals(newUserAgent))
                && day <= maxAge) {
            // 如果非记住密码（非自动登录），单次登陆有效时间验证
            if (!autoLogin) {
                int minute = ToolDateTime.getDateMinuteSpace(start, new Date());
                int session = MyConfig.config_session_timeout;
                if (minute > session) {
                    return null;
                }
            }
            // 返回用户数据
            return adminService.selectByPrimaryKey(Integer.valueOf(adminId));
        }
        return null;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    }
}